
#pragma once

#include <map>

class GlutTimer;

class TimerAccounting
{
public:
    void regTimer(GlutTimer*);
    void unregTimer(GlutTimer*);
    GlutTimer* getTimer(int);
private:
    int generateId();
private:
    std::map<int, GlutTimer*> store;
};

class GlutTimer
{
friend class TimerAccounting;
public:
    GlutTimer();
    virtual ~GlutTimer();
    void setInterval(int millis);
    void setSingleShot(bool);
    void start();
    void stop();
    bool isRunning() const;
protected:
    virtual void onTimer() = 0;
private:
    static void GlutTimerFunction(int id);
private:
    int timeout;
    bool bIsRunning;
    bool bSingleShot;
    int id;
    static TimerAccounting tmrAcnt;
};

class FuncGlutTimer1 : public GlutTimer
{
typedef void (__cdecl *PFunc)();
public:
    FuncGlutTimer1(PFunc f) : func(f) {}
protected:
    void onTimer() {func();}
private:
    PFunc func;
};

class FuncGlutTimer2 : public GlutTimer
{
typedef void (__stdcall *PFunc)();
public:
    FuncGlutTimer2(PFunc f) : func(f) {}
protected:
    void onTimer() {func();}
private:
    PFunc func;
};

template <typename TObj>
class MethodGlutTimer : public GlutTimer
{
typedef void (TObj::*PFunc)();
public:
    MethodGlutTimer(TObj& obj, PFunc f) : m_rObj(obj), m_func(f) {}
protected:
    void onTimer() {(m_rObj.*m_func)();}
private:
    TObj& m_rObj;
    PFunc m_func;
};

class GlutTimerFactory
{
public:
    static GlutTimer* create(void (__cdecl *PFunc)(), int millis, bool bSingleShot = false) {
        GlutTimer* pTimer = new FuncGlutTimer1(PFunc);
        pTimer->setInterval(millis);
        pTimer->setSingleShot(bSingleShot);
        return pTimer;
    }

    static GlutTimer* create(void (__stdcall *PFunc)(), int millis, bool bSingleShot = false) {
        GlutTimer* pTimer = new FuncGlutTimer2(PFunc);
        pTimer->setInterval(millis);
        pTimer->setSingleShot(bSingleShot);
        return pTimer;
    }

    template <typename TObj>
    static GlutTimer* create(TObj& obj, void (TObj::*PFunc)(), int millis, bool bSingleShot = false) {
        GlutTimer* pTimer = new MethodGlutTimer(obj, PFunc);
        pTimer->setInterval(millis);
        pTimer->setSingleShot(bSingleShot);
        return pTimer;
    }
};





